package com.gitee.wsl.collections.bag

import com.gitee.wsl.collections.PredicatedCollection
import com.gitee.wsl.collections.bag.api.Bag
import com.gitee.wsl.func.predicate.Predicate

/**
 * Decorates another [Bag] to validate that additions
 * match a specified predicate.
 *
 *
 * This bag exists to provide validation for the decorated bag.
 * It is normally created to decorate an empty bag.
 * If an object cannot be added to the bag, an [IllegalArgumentException] is thrown.
 *
 *
 *
 * One usage would be to ensure that no null entries are added to the bag.
 *
 * <pre>
 * Bag bag = PredicatedBag.predicatedBag(new HashBag(), NotNullPredicate.INSTANCE);
</pre> *
 *
 *
 * This class is Serializable from Commons Collections 3.1.
 *
 *
 * @param <E> the type of elements in this bag
 * @since 3.0
</E> */
 class PredicatedBag<E>(bag: Bag<E>,predicate: Predicate<E>) : PredicatedCollection<E>(bag, predicate), Bag<E>{
    override fun isEmpty(): Boolean {
        return decorated().isEmpty()
    }

    override fun add(element: E, nCopies: Int): Boolean {
        validate(element)
        return decorated().add(element, nCopies)
    }

    /**
     * Gets the decorated bag.
     *
     * @return the decorated bag
     */
    protected  fun decorated(): Bag<E> {
        return super.delegate as Bag<E>
    }

    override fun equals(other: Any?): Boolean {
        return other === this || decorated() == other
    }

    override fun getCount(element: E): Int {
        return decorated().getCount(element)
    }

    override fun remove(element: E, nCopies: Int): Boolean {
        return decorated().remove(element, nCopies)
    }

    override fun uniqueSet(): Set<E> = decorated().uniqueSet()

    override fun hashCode(): Int {
        return decorated().hashCode()
    }


    companion object {

        /**
         * Factory method to create a predicated (validating) bag.
         *
         *
         * If there are any elements already in the bag being decorated, they
         * are validated.
         *
         * @param <E> the type of the elements in the bag
         * @param bag  the bag to decorate, must not be null
         * @param predicate  the predicate to use for validation, must not be null
         * @return a new predicated Bag
         * @throws NullPointerException if bag or predicate is null
         * @throws IllegalArgumentException if the bag contains invalid elements
         * @since 4.0
        </E> */
        fun <E> predicatedBag(bag: Bag<E>, predicate: Predicate<E>): PredicatedBag<E> {
            return PredicatedBag(bag, predicate)
        }
    }
}
